﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using BoolanElement.RedoUndo;
using BoolanElement.Storage;
using BoolanSlide.Document.Render;
using BoolanSlide.Document.Storage;
using BoolanSlide.FileManager;
using Configuration;
using Windows.Data.Xml.Dom;
using Windows.Foundation;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;


namespace BoolanElement
{

    public delegate void SaveOriginalElement(BooElement booElement, BooUIBase booUI, UIElementOperation uiElementOperation = UIElementOperation.Internal);
    /// <summary>
    /// 每张PPT
    /// </summary>
    public class BooSlide:IDisposable
    {
        /// <summary>
        /// 一张slide的元素列表
        /// </summary>
        public List<BooElement> Elements;

        /// <summary>
        /// Manager the ui for Slide
        /// </summary>
        public BooSlideRender Render;

        public BooDocument Document;

        public string BackgroundImagePath;

        public Color DefaultFontColor = Colors.Black;

        private BPTFileInformation.FileRatio fileRatio;
        public BPTFileInformation.FileRatio FileRatio
        {
            get
            {
                return fileRatio;
            }
            set
            {
                fileRatio = value;
            }
        }

        public enum ThumbFor
        {
            ThumbNails = 0,
            TemplateItem = 1,
            ThumbForPlay = 2,
            Cover = 3
        }
//        public delegate void LostFocus();

        public LostFocus UILostFocus;

        /// <summary>
        /// 用于标记每个元件的ID,它们应该是不同的
        /// </summary>
        private int ElementID { get; set; }

        private RedoUndoForSlide redoUndoForSlide;

        public BooSlide()
        {
            Render = new BooSlideRender();
            this.Elements = new List<BooElement>();

            BackgroundImagePath = "";

            redoUndoForSlide = new RedoUndoForSlide();
            redoUndoForSlide.addOrRemoveForUndo = this.AddOrRemoveForUndo;
            redoUndoForSlide.addOrRemoveForRedo = this.AddOrRemoveForRedo;
        }

        public void AttachToDocument(BooDocument document)
        {
            fileRatio = document.FileInformation.Resolution;
        }

        public bool Initialize()
        {
            foreach (BooElement ele in Elements)
            {
                ele.Parent = this;
            }
            return true;
        }

        public bool SetBkgImage(StorageFile BkgImage)
        {
            if (BkgImage == null)
            {
                BackgroundImagePath = "";
                return false;
            }
            BackgroundImagePath = BkgImage.Name;
            return true;
        }

        /// <summary>
        /// 保存一张slide
        /// </summary>
        /// <returns></returns>
        public XDocument Save()
        {
            XDocument ret = new XDocument();
            ret.Add(new XElement("BoolanPPT"));
            ret.Element("BoolanPPT").SetAttributeValue("Background", BackgroundImagePath);
            ret.Element("BoolanPPT").SetAttributeValue("DefaultFontColor", DefaultFontColor.ToString().Substring(1));
            foreach (BooElement element in Elements)
            {
                XElement elem = element.SaveToXElement();
                ret.Element("BoolanPPT").Add(elem);
            }
            return ret;
        }

        public async Task<bool> AddElement(BooUIBase ui,BooElement booElement)
        {
            booElement.Parent = this;
            ElementID = Elements.Count;
            booElement.UniqueID = ElementID;
            booElement.ElementTransform.ZIndex = ElementID;
            UILostFocus += ui.LostFocus;

            await Add(ui, booElement);

            SaveForRedoUndo(booElement.Clone(), ui, UIElementOperation.Add);

            Canvas.SetZIndex((UIElement)ui.BooCanvas, ElementID);

            return true;
        }

        public async void RemoveElement(BooUIBase uiElement)
        {
            await StorageLock.SlideShowOperationWaitSignal.WaitAsync();

            SaveForRedoUndo(uiElement.Element, uiElement, UIElementOperation.Remove);

            Remove(uiElement);

            StorageLock.SlideShowOperationWaitSignal.Release();
        }

        public async Task<bool> LoadSlide(Canvas canvas)
        {
            await StorageLock.SlideShowOperationWaitSignal.WaitAsync();
            await SetCanvasBkg(canvas, UIMargin.MainCanvasRect.Width,
                   UIMargin.MainCanvasRect.Height);


            if (Render.ElementsUI.Count == 0)
            {
                foreach (BooElement elem in Elements)
                {
                    BooUIBase ui = await elem.Render(this.SaveOriginalState, this.SaveForRedoUndo);
                    Render.ElementsUI.Add(ui);
                    UILostFocus += ui.LostFocus;
                } 
            }

            foreach (BooUIBase uiBase in Render.ElementsUI)
            {
                canvas.Children.Add(uiBase.BooCanvas);
            }

            StorageLock.SlideShowOperationWaitSignal.Release();
            return true;
        }        

        public async Task<bool> ChangeBkgImage(StorageFile imageFile)
        {
            StorageFile file = await ImageManager.AddImage(imageFile);
            SetBkgImage(file);


            if(fileRatio== BPTFileInformation.FileRatio.Ratio4_3)
            {
                await SetCanvasBkg(Render.ThumbNail, 
                        UIMargin.ThumbNailsWidth4_3,
                        UIMargin.ThumbNailsHeight4_3);

               
            }
            else if(fileRatio== BPTFileInformation.FileRatio.Ratio16_9) 
            {
                await SetCanvasBkg(Render.ThumbNail,
                   UIMargin.ThumbNailsWidth16_9,
                   UIMargin.ThumbNailsHeight16_9);

            }

            await SetCanvasBkg(GlobalVariable.MainCanvas, UIMargin.MainCanvasRect.Width,
                   UIMargin.MainCanvasRect.Height);

            return true;
        }

        public void SaveForRedoUndo(BooElement booElement, BooUIBase booUI, UIElementOperation uiElementOperation = UIElementOperation.Internal)
        {
            if (booUI.NeedSaveOriginalState == true)
            {
                foreach (var item in redoUndoForSlide.OriginalStatus)
                {
                    if (item.uiElement == booUI)
                    {
                        if (RedoUndoForDocument.SaveForUndo(this, SlideOperation.Original, new int[] { 0 }) == true)
                        {
                            redoUndoForSlide.SaveStatus(item.booElement, item.uiElement, UIElementOperation.Internal);
                            booUI.NeedSaveOriginalState = false;
                        }
                    }
                }
            }
            if (RedoUndoForDocument.SaveForUndo(this, SlideOperation.Child, new int[] { 0 }) == true)
                redoUndoForSlide.SaveStatus(booElement, booUI, uiElementOperation);
        }
       
        public void SaveOriginalState(BooElement booElement, BooUIBase booUI, UIElementOperation uiElementOperation = UIElementOperation.Internal)
        {
            redoUndoForSlide.InitializeSlide(booElement, booUI);
        }
       
        public async Task<bool> Undo()
        {
            await redoUndoForSlide.Undo();
            return true;
        }

        public async Task<bool> Redo()
        {
            await redoUndoForSlide.Redo();
            return true;
        }

        /// <summary>
        /// 用于在RedoUndo时恢复元件，比如添加的需要删除，删除的则需要删除
        /// </summary>
        /// <param name="uiElementOperation">RedoUndo时保存的操作，此时需要进行逆操作</param>
        public async Task<bool> AddOrRemoveForUndo(BooUIBase booUI, BooElement booElement, UIElementOperation uiElementOperation)
        {
            await StorageLock.SlideShowOperationWaitSignal.WaitAsync();
            if (uiElementOperation == UIElementOperation.Add)
            {
                Remove(booUI);
            }
            else if (uiElementOperation == UIElementOperation.Remove)
            {
                GlobalVariable.MainCanvas.Children.Add(booUI.BooCanvas);
                await Add(booUI, booElement);         
            }
            StorageLock.SlideShowOperationWaitSignal.Release();
            return true;
        }

        public async Task<bool> AddOrRemoveForRedo(BooUIBase booUI, BooElement booElement, UIElementOperation uiElementOperation)
        {
            await StorageLock.SlideShowOperationWaitSignal.WaitAsync();
            if (uiElementOperation == UIElementOperation.Add)
            {
                await Add(booUI, booElement);

                GlobalVariable.MainCanvas.Children.Add(booUI.BooCanvas);
            }
            else if (uiElementOperation == UIElementOperation.Remove)
            {
                Remove(booUI);
            }
            StorageLock.SlideShowOperationWaitSignal.Release();
            return true;
        }

        public String DebugOutput()
        {
            String Output = "";
            foreach (BooElement element in Elements)
            {
                Output += element.ToString();
            }
            return Output;
        }

        public async Task<Canvas> GetThumbNailsFromSlide(ThumbFor thumbFor = ThumbFor.ThumbNails)
        {
            
            Render.ThumbNail= await CreateThumbNails();
            Size OriginalSize = GetOriginalSize(fileRatio);
            Size DestinationSize = GetDestinationSize(thumbFor, fileRatio);

            SetThumbToSize(Render.ThumbNail, OriginalSize, DestinationSize);

            return Render.ThumbNail;
        }

        public async Task<Canvas> GetThumbNailsAtSize(Size destinationSize)
        {
            Canvas canvas = new Canvas();

            Size OriginalSize = GetOriginalSize(fileRatio);
            Size DestinationSize = destinationSize;
            

            foreach (var item in Elements)
            {
                BooUIBase ui = await item.RenderForThumbNails();
                canvas.Children.Add(ui.BooCanvas);
            }

            SetThumbToSize(canvas, OriginalSize, DestinationSize);

            await SetCanvasBkg(canvas, destinationSize.Width,destinationSize.Height);
            //await SetCanvasBkg(canvas);
            return canvas;
        }

        public async Task<Canvas> CreateThumbNails()
        {
            Canvas thumb = new Canvas();
            thumb.Background = new SolidColorBrush(Color.FromArgb(255, 255, 255, 255));

            foreach (var item in Elements)
            {
                BooUIBase ui = await item.RenderForThumbNails();
                thumb.Children.Add(ui.BooCanvas);
            }


            if (fileRatio == BPTFileInformation.FileRatio.Ratio4_3)
            {
                await SetCanvasBkg(thumb,
                        UIMargin.ThumbNailsWidth4_3,
                        UIMargin.ThumbNailsHeight4_3);

            }
            else if (fileRatio == BPTFileInformation.FileRatio.Ratio16_9)
            {
                await SetCanvasBkg(thumb,
                   UIMargin.ThumbNailsWidth16_9,
                   UIMargin.ThumbNailsHeight16_9);

            }
            return thumb;
        }

        private void SetThumbToSize(Canvas canvas, Size OriginalSize, Size DestinationSize)
        {
            canvas.Width = OriginalSize.Width;
            canvas.Height = OriginalSize.Height;

            canvas.Clip = new RectangleGeometry();
            canvas.Clip.Rect = new Rect(0, 0, canvas.Width, canvas.Height);

            //canvas.ManipulationMode = Windows.UI.Xaml.Input.ManipulationModes.Scale;
            canvas.RenderTransform = new Windows.UI.Xaml.Media.ScaleTransform()
            {
                ScaleX = DestinationSize.Width / OriginalSize.Width,
                ScaleY = DestinationSize.Height / OriginalSize.Height
            };
            //canvas.ManipulationMode = Windows.UI.Xaml.Input.ManipulationModes.None;
        }

        private Size GetOriginalSize(Storage.BPTFileInformation.FileRatio fileRatio)
        {
            Size OriginalSize = new Size();
            if (fileRatio == Storage.BPTFileInformation.FileRatio.Ratio16_9)
            {
                OriginalSize.Width = UIMargin.SlideWidth16_9;
                OriginalSize.Height = UIMargin.SlideHeight16_9;
            }
            else if (fileRatio == Storage.BPTFileInformation.FileRatio.Ratio4_3)
            {
                OriginalSize.Width = UIMargin.SlideWidth4_3;
                OriginalSize.Height = UIMargin.SlideHeight4_3;
            }
            return OriginalSize;
        }

        private Size GetDestinationSize(ThumbFor thumbFor, Storage.BPTFileInformation.FileRatio fileRatio)
        {
            Size DestinationSize = new Size();
            if (fileRatio == Storage.BPTFileInformation.FileRatio.Ratio16_9)
            {
                if (thumbFor == ThumbFor.ThumbNails)
                {
                    DestinationSize.Width = UIMargin.ThumbNailsWidth16_9;
                    DestinationSize.Height = UIMargin.ThumbNailsHeight16_9;
                }
                else if (thumbFor == ThumbFor.TemplateItem)
                {
                    DestinationSize.Width = UIMargin.TemplateItemWidth16_9;
                    DestinationSize.Height = UIMargin.TemplateItemHeight16_9;
                }
                else if (thumbFor == ThumbFor.ThumbForPlay)
                { 
                    DestinationSize.Width = UIMargin.ThumbNailsForPlayWidth16_9;
                    DestinationSize.Height = UIMargin.ThumbNailsForPlayHeight16_9;
                }
                else if (thumbFor == ThumbFor.Cover)
                {
                    DestinationSize.Width = UIMargin.CoverWidth16_9;
                    DestinationSize.Height = UIMargin.CoverHeight16_9;
                }
            }
            else if (fileRatio == Storage.BPTFileInformation.FileRatio.Ratio4_3)
            {
                if (thumbFor == ThumbFor.ThumbNails)
                {
                    DestinationSize.Width = UIMargin.ThumbNailsWidth4_3;
                    DestinationSize.Height = UIMargin.ThumbNailsHeight4_3;
                }
                else if (thumbFor == ThumbFor.TemplateItem)
                {
                    DestinationSize.Width = UIMargin.TemplateItemWidth4_3;
                    DestinationSize.Height = UIMargin.TemplateItemHeight4_3;
                }
                else if (thumbFor == ThumbFor.ThumbForPlay)
                {
                    DestinationSize.Width = UIMargin.ThumbNailsForPlayWidth4_3;
                    DestinationSize.Height = UIMargin.ThumbNailsForPlayHeight4_3;
                }
                else if (thumbFor == ThumbFor.Cover)
                {
                    DestinationSize.Width = UIMargin.CoverWidth4_3;
                    DestinationSize.Height = UIMargin.CoverHeight4_3; 
                }
            }

            return DestinationSize;
        }

        private async Task<bool> SetCanvasBkg(Canvas canvas, double width, double height)
        {
            if (BackgroundImagePath != null && BackgroundImagePath != "")
            {
                 BitmapImage bmp=null;
             
                 ImageBrush ImgBrush =null;
                 if (Configuration.GlobalVariable.ImageBrushPool.ContainsKey(BackgroundImagePath+width+"-"+height))
                 {
                     ImgBrush = Configuration.GlobalVariable.ImageBrushPool[BackgroundImagePath + width + "-" + height];
                 }
                 else
                 {
                     StorageFile imageFile = await ImageManager.GetImage(BackgroundImagePath);
                     bmp = await BooUtility.GetBitmapImage(imageFile);
                     bmp.DecodePixelWidth = (int)width;
                     bmp.DecodePixelHeight = (int)height;
                     ImgBrush = new ImageBrush();
                     ImgBrush.ImageSource = bmp;
                     ImgBrush.Stretch = Stretch.Fill;
                     Configuration.GlobalVariable.ImageBrushPool[BackgroundImagePath + width + "-" + height] = ImgBrush;
                 }

                 canvas.Background = ImgBrush;

            }
            else
                canvas.Background = new SolidColorBrush(Color.FromArgb(255, 255, 255, 255));

            return true;
        }
              


        private void Remove(BooUIBase uiElement)
        {
            for (int i = 0; i < GlobalVariable.MainCanvas.Children.Count; i++)
            {
                if (GlobalVariable.MainCanvas.Children[i] == uiElement.BooCanvas)
                {
                    GlobalVariable.MainCanvas.Children.RemoveAt(i);
                    Render.ThumbNail.Children.RemoveAt(i);
                    Render.ElementsUI.RemoveAt(i); 
                    Elements.RemoveAt(i);
                    break;
                }
            }

            for (int i = 0; i < Render.ElementsUI.Count; i++)
            {
                Render.ElementsUI[i].Element.ElementTransform.ZIndex = i;
                Canvas.SetZIndex(Render.ElementsUI[i].BooCanvas, i);
            }
        }

        private async Task<bool> Add(BooUIBase uiElement, BooElement booElement)
        {
            Elements.Add(booElement);
            Render.ElementsUI.Add(uiElement);


            BooUIBase uiThumbNail = await uiElement.Element.RenderForThumbNails();
            Render.ThumbNail.Children.Add(uiThumbNail.BooCanvas);

            return true;
        }

        public void Dispose()
        {
            foreach (var item in Render.ElementsUI)
            {
                if (item is BooTextBox)
                {
                    (item as BooTextBox).Dispose();
                }
            }
        }
    }
}
